<?php

namespace Tools\Rpc\Stream;

/**
 * Class Server
 */
class Server
{
    /**
     * @var false|resource|null
     */
    protected $server = null;

    /**
     * @var array ['service' => ['method'...]]
     */
    protected $serviceList = [];

    /** @var string  */
    protected $serviceErrorMessage = 'Not find service!';

    /**
     * RpcServer constructor.
     * @param string $host
     * @param int $port
     * @throws \Exception
     */
    public function __construct(string $host, int $port)
    {
        $this->server = stream_socket_server("tcp://{$host}:{$port}", $errorCode, $errorMessage);
        if (false === $this->server) {
            throw new \Exception("{$errorCode} : {$errorMessage}" . PHP_EOL);
        }
    }

    /**
     * Register the object all public method
     * @param $object
     */
    public function registerService($object)
    {
        $service = get_class($object);
        $methodList = get_class_methods($object);

        foreach ($methodList as $method) {
            $this->serviceList[$service][] = $method;
        }

        $this->serviceList[$service] = array_unique($this->serviceList[$service]);
    }

    /**
     * @throws \Exception
     */
    public function run()
    {
        while (true) {
            $client = stream_socket_accept($this->server, -1);
            if ($client) {
                $buffer = fread($client, 2048);

                /*
                 * ['service' => ?, 'method' => ?, 'parameters' => ?]
                 */
                $requestData = json_decode($buffer, true);

                if (isset($this->serviceList[$requestData['service']])
                    && in_array($requestData['method'], $this->serviceList[$requestData['service']])) {
                    $service = new ($requestData['service']);
                    $result = $service->{$requestData['method']}(...$requestData['parameters']);
                } else {
                    $result = $this->serviceErrorMessage;
                }

                fwrite($client, $result);

                fclose($client);
            }
        }
    }

    /**
     *
     */
    public function __destruct()
    {
        fclose($this->server);
    }
}

